import log from "./dxLogger.js";
import std from "./dxStd.js";
import dxMap from "./dxMap.js";
import dxNetwork from "./dxNetwork.js";
import httpclient from "./dxHttpClient.js";
import dxEventBus from "./dxEventBus.js";

/**
 * Screenshot Upload Worker
 *
 * This worker runs in a background thread and is responsible for:
 * - Periodically scanning a local screenshot directory
 * - Uploading screenshot files to a remote HTTP endpoint
 * - Deleting local files after a successful upload
 *
 * Features:
 * - Reads configuration from a shared dxMap entry (topic: "default")
 * - Optional automatic network initialization via dxNetwork when `autoNet` is enabled
 * - Supports both Ethernet (DHCP) and Wi-Fi (DHCP only)
 *
 * Configuration:
 * - The worker expects its configuration to be stored under the key `__screenshot_worker_config__`
 *   in `dxMap.get("default")`. Typical configuration object:
 *
 *   {
 *     uploadUrl: "http://server/upload", // HTTP endpoint for file upload (required)
 *     autoNet: false,                    // true: worker manages network; false: network managed elsewhere
 *     netType: "eth" | "wifi",           // used only when autoNet === true
 *     ssid: "MyWifi",                    // Wi-Fi SSID (for wifi mode)
 *     password: "MyPassword"             // Wi-Fi password (for wifi mode)
 *   }
 *
 * Lifecycle:
 * - This file is intended to be run as a worker script, created via std.Worker(...)
 * - Once started, it will scan the screenshot directory every 5 seconds
 * - Network initialization (if enabled) is performed only once on the first scan
 */

const SNAPSHOT_DIR = "/app/data/snapshot/";
const GLOBAL_TOPIC = "default";
const KEY_WORKER_CONFIG = "__screenshot_worker_config__";
const TOPIC_SCREENSHOT_ENQUEUE = "__screenshot_enqueue__";

// Upload queue: keep at most the latest 10 screenshot file names.
const MAX_QUEUE_LEN = 10;
const uploadQueue = [];

const map = dxMap.get(GLOBAL_TOPIC);
let netInited = false;

/**
 * Ensures that network connectivity is initialized when `autoNet` is enabled.
 *
 * Behavior:
 * - If `cfg.autoNet` is false or missing, this function does nothing and assumes
 *   that the application manages network connectivity elsewhere.
 * - If `cfg.autoNet` is true:
 *   - Initializes the network stack via dxNetwork.init()
 *   - Connects to Ethernet with DHCP, or
 *   - Connects to Wi-Fi with DHCP using `cfg.ssid` / `cfg.password`
 *
 * @param {object} cfg - Configuration object read from dxMap.
 */
function ensureNetworkIfNeeded(cfg) {
    // If autoNet is disabled, do nothing and rely on external network management.
    if (!cfg || !cfg.autoNet) {
        return;
    }

    try {
        dxNetwork.init();
    } catch (e) {
        log.error("screenshotworker: dxNetwork.init failed", e);
    }

    try {
        // DHCP only: choose Ethernet or Wi-Fi based on configuration
        const netType = (cfg.netType || "").toLowerCase();
        if (netType === "wifi") {
            if (!cfg.ssid || !cfg.password) {
                log.error("screenshotworker: Wi-Fi mode requires ssid and password");
                return;
            }
            dxNetwork.connectWifiWithDHCP(cfg.ssid, cfg.password);
            log.info("screenshotworker: Wi-Fi DHCP connection requested", cfg.ssid);
        } else {
            // Default to Ethernet DHCP when netType is missing or not "wifi"
            dxNetwork.connectEthWithDHCP();
            log.info("screenshotworker: Ethernet DHCP connection requested");
        }
    } catch (e) {
        log.error("screenshotworker: dxNetwork connect failed", e);
    }
}

/**
 * Uploads a single screenshot file and deletes it on success.
 *
 * @param {string} fullPath - Absolute path of the screenshot file to upload.
 * @param {object} cfg - Configuration object containing `uploadUrl`.
 */
function uploadOneFile(fullPath, cfg) {
    if (!cfg || !cfg.uploadUrl) {
        log.warn("screenshotworker: uploadUrl not configured, skip file", fullPath);
        return false;
    }

    try {
        // Note: this call is synchronous in the current implementation.
        const res = httpclient.upload(cfg.uploadUrl, fullPath, 30000);

        const ok =
            res &&
            (res.status === 200 ||
                res.status === 201 ||
                res.code === 0 ||
                res.ok === true);

        if (ok) {
            const rmCode = std.remove(fullPath);
            if (rmCode === 0) {
                log.info("screenshotworker: uploaded and removed file", fullPath);
            } else {
                log.error(
                    "screenshotworker: uploaded but failed to remove file",
                    fullPath,
                    "code=",
                    rmCode
                );
            }
            return true;
        } else {
            log.error("screenshotworker: upload failed", fullPath, res);
            return false;
        }
    } catch (e) {
        log.error("screenshotworker: upload threw exception", fullPath, e);
        return false;
    }
}

/**
 * Uploads files from the in-memory queue.
 *
 * Steps:
 * 1. Load configuration from dxMap.
 * 2. If `autoNet` is enabled and network is not yet initialized, initialize it once.
 * 3. Iterate the uploadQueue and try to upload files.
 * 4. On success: delete local file (handled in uploadOneFile) and remove it from queue.
 */
function scanAndUpload() {
    let cfg = {};
    try {
        cfg = map.get(KEY_WORKER_CONFIG) || {};
    } catch (e) {
        log.error("screenshotworker: failed to read config from dxMap", e);
    }

    // Perform network initialization at most once when autoNet is enabled.
    if (!netInited && cfg && cfg.autoNet) {
        ensureNetworkIfNeeded(cfg);
        netInited = true;
    }

    if (!uploadQueue.length) {
        return;
    }

    // FIFO: upload from the head; pop on success, keep on failure for retry on next tick.
    for (let i = 0; i < uploadQueue.length;) {
        const name = uploadQueue[i];
        if (!name) {
            uploadQueue.splice(i, 1);
            continue;
        }
        const fullPath = SNAPSHOT_DIR + name;
        const ok = uploadOneFile(fullPath, cfg);
        if (ok) {
            uploadQueue.splice(i, 1);
        } else {
            i++;
        }
    }
}

function enqueueFileName(fileName) {
    if (!fileName || typeof fileName !== "string") return;
    uploadQueue.push(fileName);

    // Enforce max length: drop the oldest entries first.
    while (uploadQueue.length > MAX_QUEUE_LEN) {
        uploadQueue.shift();
    }
}

// Subscribe to main-thread notifications: enqueue screenshot file names.
dxEventBus.on(TOPIC_SCREENSHOT_ENQUEUE, (msg) => {
    const fileName = msg && msg.fileName;
    enqueueFileName(fileName);
});

// Periodically scan the screenshot directory and try to upload files
std.setInterval(scanAndUpload, 5000, true);

log.info("screenshotworker: started, queue mode, snapshot dir =", SNAPSHOT_DIR);